package set;

import java.util.Comparator;
import java.util.Iterator;

import array.Array;

import list.List;

public class Set<T> {
	
	private List<T> elems;
	private Comparator<T> comp;
	
	public Set(Comparator<T> comp){
		elems = new List<T>();
		this.comp = comp;
	}
	
	public Set(){
		elems = new List<T>();
		this.comp = new Comparator<T>() {
			public int compare(T o1, T o2) {
				if(o1.equals(o2)) return 0;
				else return 1;
			}
		};
	}
	
	public Set(List<T> l, Comparator<T> comp){
		elems = new List<T>();
		Iterator<T> it = l.iterator();
		while(it.hasNext()){
			T elem = it.next();
			if(!elems.searchFor(elem, comp))elems.add(elem);
		}
		this.comp = comp;
	}
	
	public Set(List<T> l){
		elems = new List<T>();
		Iterator<T> it = l.iterator();
		this.comp = new Comparator<T>() {
			public int compare(T o1, T o2) {
				if(o1.equals(o2)) return 0;
				else return 1;
			}
		};
		while(it.hasNext()){
			T elem = it.next();
			if(!elems.searchFor(elem, comp))elems.add(elem);
		}

	}
	
	public Set(Array<T> array, Comparator<T> comp) {
		elems = new List<T>();
		for(int i = 0; i < array.size(); i++){
			elems.add(array.get(i));
		}
		this.comp = comp;
	}

	public Set(Array<T> array) {
		elems = new List<T>();
		for(int i = 0; i < array.size(); i++){
			elems.add(array.get(i));
		}
		this.comp = new Comparator<T>() {
			public int compare(T o1, T o2) {
				if(o1.equals(o2)) return 0;
				else return 1;
			}
		};
	}
	
	public void add(T elem){
		if(!elems.searchFor(elem, comp))elems.add(elem);
	}
	
	public void remove(T elem){
		if(elems.searchFor(elem, comp))elems.remove(elem, comp);
	}
	
	public Set<T> union(Set<T> s){
		Set<T> union = new Set<T>(elems, comp);
		Iterator<T> s2 = s.elems.iterator();
		while(s2.hasNext()){
			T elem = s2.next();
			if(union.elems.searchFor(elem, comp))union.elems.add(elem);
		}
		return union;
	}
	
	public Set<T> minus(Set<T> s){
		Set<T> minus = new Set<T>(elems, comp);
		Iterator<T> s2 = s.elems.iterator();
		while(s2.hasNext()){
			T elem = s2.next();
			if(minus.elems.searchFor(elem, comp))minus.elems.remove(elem, comp);
		}
		return minus;
	}
	
	public Set<T> intersecction(Set<T> s){
		Set<T> inter = new Set<T>(elems, comp);
		Iterator<T> s1 = elems.iterator();
		Iterator<T> s2 = s.elems.iterator();
		while(s1.hasNext()){
			T elem = s1.next();
			if(!s.elems.searchFor(elem, comp))inter.elems.remove(elem, comp);
		}
		while(s2.hasNext()){
			T elem = s2.next();
			if(!elems.searchFor(elem, comp))inter.elems.remove(elem, comp);
		}
		return inter;	
	}
	
	public Set<T> simetricDiferrence(Set<T> s){
		Set<T> simt = new Set<T>(comp);
		Iterator<T> s1 = elems.iterator();
		Iterator<T> s2 = s.elems.iterator();
		while(s1.hasNext()){
			T elem = s1.next();
			if(!s.elems.searchFor(elem, comp))simt.elems.add(elem);
		}
		while(s2.hasNext()){
			T elem = s2.next();
			if(!elems.searchFor(elem, comp))simt.elems.add(elem);
		}
		return simt;	
	}
	
	public Set<Set<T>> potency(Set<T> s){
		Comparator<Set<T>> compP = new Comparator<Set<T>>() {
			public int compare(final Set<T> o1, final Set<T> o2) {
				if(o1.isSuperSet(o2)) return 1;
				return 0;
			}
		};
		Set<Set<T>> pot;
		T elems[] = this.elems.toArray();
		int size = this.elems.size();
		pot = gereteSubset(size, elems);
		return pot;	
	}
	
	private Set<Set<T>> gereteSubset( int size, T[] elems) {
		if(size > 0){
			T elem = elems[size];
			Set<Set<T>> r = new Set<Set<T>>();
			Iterator<Set<T>> it = gereteSubset(size-1, elems).iterator();
			while(it.hasNext()){
				Set<T> rr = new Set<T>();
				rr.add(elem);
				r.add(it.next().union(rr));
			}
			Set<T> rr = new Set<T>();
			rr.add(elem);
			r.add(rr);
			return r;
		}
		return new Set<Set<T>>();
	}

	public boolean isSubset(Set<T> s){
		Iterator<T> it = elems.iterator();
		while(it.hasNext()){
			if(!s.elems.searchFor(it.next(), comp)) return false;
		}
		return true;
	}
	
	public boolean isSuperSet(Set<T> s){
		Iterator<T> it = s.elems.iterator();
		while(it.hasNext()){
			if(!elems.searchFor(it.next(), comp)) return false;
		}
		return true;
	}
	
	public Set<T> subset(Property<T> prop){
		Set<T> subset = new Set<T>(comp);
		Iterator<T> it = elems.iterator();
		while(it.hasNext()){
			T elem = it.next();
			if(prop.hasProperty(elem)) subset.elems.add(elem);
		}
		return subset;
	}
	
	public T[] toArray(){
		return elems.toArray();
	}
	
	public String toString(){
		return elems.toString();
	}
	
	public Iterator<T> iterator(){
		return elems.iterator();
	}
}
